/**
 * @file	may.h
 * @brief	包含了Json类的操作等功能
 * @author	Majjcom
 * @date	2021/11/14
 */

#ifndef __MAY_H__
#define __MAY_H__

#include "rapidjson/prettywriter.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include <string>
#include <vector>

#define U_INT unsigned int ///< 将 unsigned int 类型用 U_INT 代替

namespace mayjson
{
	// 初始化函数
	inline std::string fromObjectCreateDocument(rapidjson::GenericObject<true, rapidjson::Value::ValueType> obj, std::vector<rapidjson::Document*>& pool);

	/**
	 * \brief	Json类
	 * 定义Json类，用于Json的读取，分析，修改，输出等工作
	 */
	class Json
	{
	private:
		
		//! 定义基本Object，用于存放Json实例
		rapidjson::Document _object;

		//! 内存处理对象池存放类外生成的Document内存地址，方便管理
		std::vector<rapidjson::Document*> _pool;

		/**
		 * \brief	私有_setObject函数
		 * \param	jsonStr		传入需要解析的Json对象字符串
		 */
		void _setObject(const char jsonStr[])
		{
			rapidjson::Document& d = this->_object;
			d.Parse(jsonStr);
		}

		/**
		 * \brief	私有_setObject函数重载
		 * \param	documentObject	传入需要解析的Json Document类
		 */
		void _setObject(rapidjson::Document& documentObject)
		{
			using rapidjson::StringBuffer;
			using rapidjson::Writer;

			StringBuffer buffer;
			Writer<StringBuffer> writer(buffer);
			documentObject.Accept(writer);
			const char* get = buffer.GetString();
			this->_setObject(get);
		}

		/**
		 * \brief	私有_cleanUp函数，用于清理对象内存地址
		 */
		void _cleanUp()
		{
			std::vector<rapidjson::Document*>& pool = this->_pool;
			for (U_INT i = 0; i < unsigned(pool.size()); i++)
			{
				delete pool[i];
			}
		}

	public:
		
		/**
		 * \brief	setUp函数\n
		 * 在创建对象后设定原始Json;
		 * \param	jsonStr		传入Json对象字符串
		 * \return	返回0
		 */
		int setUp(const char jsonStr[])
		{
			this->_setObject(jsonStr);
			return 0;
		}

		/**
		 * \brief	setUp函数\n
		 * 在创建对象后设定原始Json;
		 * \param	jsonStr		传入string类型的Json对象字符串
		 * \return	返回0
		 */
		int setUp(const std::string jsonStr)
		{
			const char* str = jsonStr.c_str();
			this->_setObject(str);
			return 0;
		}

		/**
		 * \brief	setUp函数\n
		 * 在创建对象后设定原始Json;
		 * \param	docu	传入Document Json对象字符串
		 * \return	返回0
		 */
		int setUp(rapidjson::Document& docu)
		{
			this->_setObject(docu);
			return 0;
		}

		int setPool(std::vector<rapidjson::Document*>& pool)
		{
			if (this->_pool.size() == 0)
			{
				this->_pool = pool;
				return 0;
			}
			return -1;
		}

		/**
		 * \brief	existh函数\n
		 * 用于检测Json对象是否存在
		 * \param	index	传入对象名字符串
		 * \return	返回是否存在
		 *  \retval true	Json中存在此对象
		 *  \retval false	Json中不存在此对象
		 */
		bool exist(const char index[])
		{
			rapidjson::Document& d = this->_object;
			return d.HasMember(index);
		}

		/**
		 * \brief	getType函数\n
		 * 用于确定Json对象类型
		 * \param	index 
		 * \return	返回字符串类型数据，返回类型名称
		 */
		const char* getType(const char index[])
		{
			rapidjson::Document& d = this->_object;
			rapidjson::Type t = d[index].GetType();
			switch (t)
			{
			case rapidjson::Type::kObjectType:
				return "Object";
				break;
			case rapidjson::Type::kNumberType:
				if (d[index].IsInt())
				{
					return "Int";
				}
				else if (d[index].IsFloat())
				{
					return "Float";
				}
				else if (d[index].IsDouble())
				{
					return "Double";
				}
				break;
			case rapidjson::Type::kStringType:
				return "String";
				break;
			case rapidjson::Type::kTrueType:
			case rapidjson::Type::kFalseType:
				return "Bool";
				break;
			case rapidjson::Type::kNullType:
				return "Null";
				break;
			case rapidjson::Type::kArrayType:
				return "Array";
				break;
			}
			return "Unknown";
		}

		template <typename T>
		T getValue(const char index[]);

		/**
		 * \brief	getValue函数\n
		 * 获取Int类型数据
		 * \param	index	传入对象索引字符串
		 * \return	返回Int类型数据
		 */
		template <>
		int getValue<int>(const char index[])
		{
			const rapidjson::Document& d = this->_object;
			return d[index].GetInt();
		}

		/**
		 * \brief	getValue函数\n
		 * 获取Float类型数据
		 * \param	index	传入对象索引字符串
		 * \return	返回Float类型数据
		 */
		template <>
		float getValue<float>(const char index[])
		{
			const rapidjson::Document& d = this->_object;
			return d[index].GetFloat();
		}

		/**
		 * \brief	getValue函数\n
		 * 获取Double类型数据
		 * \param	index	传入对象索引字符串
		 * \return	返回Double类型数据
		 */
		template <>
		double getValue<double>(const char index[])
		{
			const rapidjson::Document& d = this->_object;
			return d[index].GetDouble();
		}

		/**
		 * \brief	getValue函数\n
		 * 获取字符串类型数据
		 * \param	index	传入对象索引字符串
		 * \return	返回 const char* 类型数据
		 */
		template <>
		const char* getValue<const char*>(const char index[])
		{
			const rapidjson::Document& d = this->_object;
			return d[index].GetString();
		}

		/**
		 * \brief	GetValue函数\n
		 * 获取Bool类型数据
		 * \param	index	传入对象索引字符串
		 * \return	返回Bool类型数据
		 */
		template <>
		bool getValue<bool>(const char index[])
		{
			const rapidjson::Document& d = this->_object;
			return d[index].GetBool();
		}

		/**
		 * \brief	getValue函数\n
		 * 获取Json类型数据
		 * \param	index	传入对象索引字符串
		 * \return	返回Json类型数据
		 */
		template <>
		Json getValue<Json>(const char index[])
		{
			const rapidjson::Document& d = this->_object;
			std::vector<rapidjson::Document*> pool;
			std::string d_new_str = fromObjectCreateDocument(d[index].GetObject(), pool);
			rapidjson::Document d_new;
			d_new.Parse(d_new_str.c_str());
			Json ret;
			ret.setUp(d_new);
			ret.setPool(pool);
			return ret;
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定Int类型数据
		 * \param	index	传入数据索引字符串
		 * \param	value	传入Int类型数据
		 */
		void setValue(const char index[], const int value)
		{
			rapidjson::Document& d = this->_object;
			if (this->exist(index))
			{
				d[index].SetInt(value);
			}
			else
			{
				rapidjson::Value::StringRefType name(index);
				rapidjson::Value v;
				v.SetInt(value);
				d.AddMember(name, v, d.GetAllocator());
			}
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定Float类型数据
		 * \param	index	传入数据索引字符串
		 * \param	value	传入Float类型数据
		 */
		void setValue(const char index[], const float value)
		{
			rapidjson::Document& d = this->_object;
			if (this->exist(index))
			{
				d[index].SetFloat(value);
			}
			else
			{
				rapidjson::Value::StringRefType name(index);
				rapidjson::Value v;
				v.SetFloat(value);
				d.AddMember(name, v, d.GetAllocator());
			}
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定Double类型数据
		 * \param	index	传入数据索引字符串
		 * \param	value	传入Double类型数据
		 */
		void setValue(const char index[], const double value)
		{
			rapidjson::Document& d = this->_object;
			if (this->exist(index))
			{
				d[index].SetDouble(value);
			}
			else
			{
				rapidjson::Value::StringRefType name(index);
				rapidjson::Value v;
				v.SetDouble(value);
				d.AddMember(name, v, d.GetAllocator());
			}
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定字符转类型数据
		 * \param	index	传入数据索引字符串
		 * \param	value	传入 const char* 类型数据
		 */
		void setValue(const char index[], const char *value)
		{
			rapidjson::Document& d = this->_object;
			if (this->exist(index))
			{
				d[index].SetString(value, strlen(value));
			}
			else
			{
				rapidjson::Value::StringRefType name(index);
				rapidjson::Value v;
				v.SetString(value, strlen(value));
				d.AddMember(name, v, d.GetAllocator());
			}
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定Bool类型数据
		 * \param	index	传入数据索引字符串
		 * \param	value	传入Bool类型数据
		 */
		void setValue(const char index[], bool value)
		{
			rapidjson::Document& d = this->_object;
			if (this->exist(index))
			{
				d[index].SetBool(value);
			}
			else
			{
				rapidjson::Value::StringRefType name(index);
				rapidjson::Value v;
				v.SetBool(value);
				d.AddMember(name, v, d.GetAllocator());
			}
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定Null类型数据
		 * \param	index	传入数据索引字符串
		 */
		void setValue(const char index[])
		{
			rapidjson::Document& d = this->_object;
			if (this->exist(index))
			{
				d[index].SetNull();
			}
			else
			{
				rapidjson::Value::StringRefType name(index);
				rapidjson::Value v;
				v.SetNull();
				d.AddMember(name, v, d.GetAllocator());
			}
		}

		/**
		 * \brief	setValue函数\n
		 * 在指定索引位置设定Json类型数据
		 * \param	index	传入数据索引字符串
		 * \param	value	传入Json类型数据
		 */
		void setValue(const char index[], Json &value)
		{
			rapidjson::Document& d = this->_object;
			rapidjson::Document* d_new = new rapidjson::Document;
			d_new->Parse(value.getStr().c_str());
			std::vector<rapidjson::Document*>& pool = this->_pool;
			pool.resize(pool.size() + 1);
			pool[pool.size() - 1] = d_new;
			if (this->exist(index))
			{
				d.EraseMember(index);
			}
			rapidjson::Value::StringRefType name(index);
			d.AddMember(name, *d_new, d.GetAllocator());
		}

		/**
		* \brief	removeValue函数\n
		* 删除指定索引位置的数据
		* \param	index	传入数据索引字符串
		* \return	返回bool类型数据
		*  \retval	true	删除成功
		*  \retval	false	删除失败
		*/
		bool removeValue(const char index[])
		{
			rapidjson::Document& d = this->_object;
			std::vector<rapidjson::Document*>& pool = this->_pool;
			if (!this->exist(index))
			{
				return false;
			}
			d.EraseMember(index);
			return true;
		}

		/**
		 * \brief	getStr函数\n
		 * 获取本实例的Json对象的字符串形式
		 * \return	返回string类型的Json字符串
		 */
		std::string getStr()
		{
			const rapidjson::Document& d = this->_object;
			using rapidjson::StringBuffer;
			using rapidjson::Writer;
			using std::string;

			StringBuffer buffer;
			Writer<StringBuffer> writer(buffer);
			d.Accept(writer);
			const char* get = buffer.GetString();
			string res(get);
			return res;
		}

		/**
		 * \brief	getPrettyStr函数\n
		 * 获取本实例的Json对象的更美观的字符串形式
		 * \return	返回string类型的更加美观的Json字符串
		 */
		std::string getPrettyStr()
		{
			const rapidjson::Document& d = this->_object;
			using rapidjson::PrettyWriter;
			using rapidjson::StringBuffer;
			using std::string;

			StringBuffer buffer;
			PrettyWriter<StringBuffer> writer(buffer);
			d.Accept(writer);
			const char* get = buffer.GetString();
			string res(get);
			return res;
		}

		/**
		 * \brief release函数
		 * 用于释放Json对象池占用的内存资源
		 */
		void release()
		{
			this->_cleanUp();
		}
	};

	/**
	 * \brief	fromObjectCreateDocument函数
	 * 用于生成Json对象中的Json对象的副本
	 * \param obj	传入Json对象的Object对象的迭代器
	 * \param pool	传入Json对象的对象池，用于内存管理
	 * \return	返回字符串类型的Json对象
	 */
	inline std::string fromObjectCreateDocument(rapidjson::GenericObject<true, rapidjson::Value::ValueType> obj, std::vector<rapidjson::Document*>& pool)
	{
		using std::string;

		rapidjson::Document d_new;
		d_new.Parse("{}");
		for (rapidjson::Value::ConstMemberIterator itr = obj.MemberBegin(); itr != obj.MemberEnd(); itr++)
		{
			const char* nameStr = itr->name.GetString();
			rapidjson::Value::StringRefType name(nameStr);
			rapidjson::Value value;
			if (itr->value.IsObject())
			{
				string v = fromObjectCreateDocument(itr->value.GetObject(), pool);
				rapidjson::Document* value = new rapidjson::Document;
				value->Parse(v.c_str());
				d_new.AddMember(name, *value, d_new.GetAllocator());
				pool.resize(pool.size() + 1);
				pool[pool.size() - 1] = value;
			}
			else
			{
				if (itr->value.IsInt())
				{
					value.SetInt(itr->value.GetInt());
				}
				else if (itr->value.IsFloat())
				{
					value.SetFloat(itr->value.GetFloat());
				}
				else if (itr->value.IsDouble())
				{
					value.SetDouble(itr->value.GetDouble());
				}
				else if (itr->value.IsString())
				{
					value.SetString(itr->value.GetString(), itr->value.GetStringLength());
				}
				d_new.AddMember(name, value, d_new.GetAllocator());
			}
		}

		rapidjson::StringBuffer buffer;
		rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
		d_new.Accept(writer);
		string dStr(buffer.GetString());
		return dStr;
	}
}

#endif